// #define DONOT_USE_RSEQ
#define _GNU_SOURCE
#include <assert.h>
#include <stdlib.h>
#include <libapfsds.h>
#include <ddblk_plat.h>
#include <dbg.h>
#include <dbg/ptest.h>
#include <pthreadalt.h>

#include <add.h>
#include <htable.h>
#include <nop.h>
#include <queue.h>
#include <stack.h>
#include <rbtree.h>
#include <tlsf.h>

#ifndef HDEF_LITTLECORE_OFFST
	#warning "undefined LITTLECORE_OFFST"
	#define HDEF_LITTLECORE_OFFST	0
#endif


/* 执行线程数 */
/* 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24 */
/* 1, 2, 4, 8, 12, 16, 20, 24, 36, 48, 60, 72 */
#ifndef TEST_THREADNR
	#define TEST_THREADNR			(HDEF_NATIVE_CPU_NR)
	#if (HDEF_NATIVE_CPU_NR == 8)
		#define WARMUP_THREAD_NR	16
		static const word_t test_nrs_arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
	#elif (HDEF_NATIVE_CPU_NR == 16)
		#define WARMUP_THREAD_NR	16
		static const word_t test_nrs_arr[] = {1, 2, 4, 6, 8, 11, 13, 16};
	#elif (HDEF_NATIVE_CPU_NR == 24)
		#define WARMUP_THREAD_NR	24
		static const word_t test_nrs_arr[] = {1, 2, 4, 8, 12, 16, 20, 24};
	#endif
#endif


#define TEST_NR		(100000)
/* 不能超过16，因为mutex之类的future有限制 */
#define TEST_LOCKS	(HDEF_NATIVE_CPU_NR >= 16? 16 : HDEF_NATIVE_CPU_NR ) 

static apfsds_t *apfsds[TEST_LOCKS];
static addcount_t addcnt;
static addcount_t addcnts[TEST_LOCKS];

#define HTABLE_SIZE (HDEF_NATIVE_CPU_NR*HDEF_NATIVE_CPU_NR)
static htable_t *htable;

static struct rb_root rbroot = {0};

static TLS_INITIAL tid_t mytid;
#define MAX_ALLOCATION_SIZE (4096)
#define TLSF_POOL_SIZE (MAX_ALLOCATION_SIZE * 64)
static tlsf_t tlsf;

static TLS_INITIAL bool_t qpush;
static TLS_INITIAL queuenode_t *qnode;
static TLS_INITIAL bool_t spush;
static TLS_INITIAL stacknode_t *snode;
static TLS_INITIAL bool_t hpush;
static TLS_INITIAL hnode_t *hnode;
static TLS_INITIAL struct mynode *rnode;
static TLS_INITIAL bool_t rpush;
static TLS_INITIAL void *mem;
static TLS_INITIAL bool_t bmalloc;


#define M	(TEST_LOCKS)

static void bench_sync_once(word_t nr){
	word_t key = mytid + HDEF_NATIVE_CPU_NR;
	word_t fut_ret[M];
	word_t fut_idx = 0;
	for (word_t idx = 0; idx < M; idx++)
	{
		WRAP_ARGS(ar, &addcnts[idx]);
		fut_ret[idx] = apfsds_sync(apfsds[idx], count_add_cb, ar);
	}

	// word_t seed = hash(nr + key);
	// word_t idx = 0;
	// do{
	// 	word_t cur = (idx + seed) % M;
	// 	WRAP_ARGS(ar, &addcnts[cur]);
	// 	fut_ret[cur] = apfsds_sync(apfsds[cur], count_add_cb, ar);
	// } while(idx++ < M);
}

static void bench_async_once(word_t nr){

	word_t key = mytid + HDEF_NATIVE_CPU_NR;
	apfsds_future_t *futs[M];
	word_t fut_idx = 0;
	for (word_t idx = 0; idx < M; idx++)
	{
		WRAP_ARGS(ar, &addcnts[idx]);
		futs[idx] = apfsds_fetch(apfsds[idx], count_add_cb, ar);
	}
	
	/* 依次等待所有结果 */
	word_t fut_ret[M];
	/* 随机? */
	word_t seed = hash(nr + key);
	word_t idx = 0;
	do{
		word_t cur = (idx + seed) % M;
		fut_ret[cur] = apfsds_wait(apfsds[cur], futs[cur]);
	} while(++idx < M);

}
/* 先sync*/
static void *bench_sync(void *args){
	word_t test_nr = (word_t)args;
	qpush = 0;
	spush = 0;
	hpush = 0;
	rpush = 0;
	if (!qnode) qnode = (queuenode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(queuenode_t));
	if (!snode) snode = (stacknode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(stacknode_t) );
	if (!hnode) hnode = (hnode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(hnode_t));
	if (!rnode) rnode = (struct mynode *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(struct mynode));
	for (word_t idx = 0; idx < test_nr; idx++){
		bench_sync_once(idx);
		// printf("%lu\n", idx);
	}
	if (qnode) free(qnode);
	if (snode) free(snode);
	if (hnode) free(hnode);
	if (rnode) free(rnode);
	qnode = NULL;
	snode = NULL;
	hnode = NULL;
	rnode = NULL;
	return NULL;
}
/* 再async*/
static void *bench_async(void *args){
	word_t test_nr = (word_t)args;
	qpush = 0;
	spush = 0;
	hpush = 0;
	rpush = 0;
	if (!qnode) qnode = (queuenode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(queuenode_t));
	if (!snode) snode = (stacknode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(stacknode_t) );
	if (!hnode) hnode = (hnode_t *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(hnode_t));
	if (!rnode) rnode = (struct mynode *)aligned_alloc(sizeof(HDEF_CACHE_LINE_BYTES), sizeof(struct mynode));
	for (word_t idx = 0; idx < test_nr; idx++){
		bench_async_once(idx);
		// printf("%lu\n", idx);
	}
	if (qnode) free(qnode);
	if (snode) free(snode);
	if (hnode) free(hnode);
	if (rnode) free(rnode);
	qnode = NULL;
	snode = NULL;
	hnode = NULL;
	rnode = NULL;
	return NULL;
}

__PERF_PARALLEL_FUNC_BINDCORE_CUSTOM(bench_sync, mytid, TEST_NR, TEST_THREADNR, HDEF_LITTLECORE_OFFST, , test_nrs_arr, dbg_printf)
__PERF_PARALLEL_FUNC_BINDCORE_CUSTOM(bench_async, mytid, TEST_NR, TEST_THREADNR, HDEF_LITTLECORE_OFFST, , test_nrs_arr, dbg_printf)

int main(){
	for (word_t idx = 0; idx < TEST_LOCKS; idx++){
		// printf("%d\n", idx);
		apfsds[idx] = apfsds_create();
	}
	

	count_init(&addcnt);
	for (word_t idx = 0; idx < TEST_LOCKS; idx++){
		count_init(&addcnts[idx]);
	}

	PERF_PARALLEL_RUN(bench_sync);
	PERF_PARALLEL_RUN(bench_async);
	
	for (word_t idx = 0; idx < TEST_LOCKS; idx++){
		apfsds_destroy(apfsds[idx]);
	}
	return 0;
}